package com.tsm.picachu.gameplay.data;

import java.util.ArrayList;
import java.util.List;

import com.util.library.common.Utils;

public class Game
{
	private int mCore = 0;
	private int mLevel = 1;
	private int mMiniLevel = 1;
	private int mTime = 0;
	private int mTotalTime = 0;

	/** Two-dimensions array of image IDs */
	private Value[][] mCellIdsArray;

	private List<Cell> mRemainCell = new ArrayList<Cell>();

	private final int TOTAL_CELL_ROWS;
	private final int TOTAL_CELL_COLUMNS;

	/** Check runnable steps */
	private Cell[] mRunnableCells = null;

	public Game(int row, int col)
	{
		TOTAL_CELL_ROWS = row;
		TOTAL_CELL_COLUMNS = col;
		mCellIdsArray = new Value[row][col];

		for (int indexRow = 0; indexRow < row; indexRow++)
		{
			for (int indexCol = 0; indexCol < col; indexCol++)
			{
				mCellIdsArray[indexRow][indexCol] = new Value();
			}
		}
	}

	public int getTotalRow()
	{
		return TOTAL_CELL_ROWS;
	}

	public int getNumberOfRemainCells()
	{
		return mRemainCell.size();
	}

	public int getTotalColumn()
	{
		return TOTAL_CELL_COLUMNS;
	}

	public int getCore()
	{
		return mCore;
	}

	public void setCore(int mCore)
	{
		this.mCore = mCore;
	}

	public int getLevel()
	{
		return mLevel;
	}

	public void setLevel(int mLevel)
	{
		this.mLevel = mLevel;
	}

	/** Get time of game in second */
	public int getTime()
	{
		return mTime;
	}

	/** Set time of game in second */
	public void setTime(int mTime)
	{
		this.mTime = mTime;
	}

	public void setValueAt(int row, int col, int value)
	{
		mCellIdsArray[row][col].setValue(value);
	}

	public int getValueAt(int row, int col)
	{
		return mCellIdsArray[row][col].getValue();
	}

	public void addRemainCell(Cell cell)
	{
		mRemainCell.add(cell);
	}

	public void removeRemainCell(Cell cell)
	{
		for (int indexRemainCell = 0; indexRemainCell < mRemainCell.size(); indexRemainCell++)
		{
			if (cell.isSame(mRemainCell.get(indexRemainCell)))
			{
				mRemainCell.remove(indexRemainCell);
				break;
			}
		}
	}

	public void removeCell(Cell cell)
	{
		mCellIdsArray[cell.getRow()][cell.getCol()].setValue(Value.VALUE_NONE);
		removeRemainCell(cell);
	}

	public boolean isRightMove(Cell lastCell, Cell currentCell)
	{
		boolean isSameValue = mCellIdsArray[lastCell.getRow()][lastCell.getCol()].getValue() == mCellIdsArray[currentCell.getRow()][currentCell.getCol()].getValue();

		// Utils.debug("isSameValue = " + isSameValue);

		if (isSameValue
				&& (isCleanBetween(lastCell, currentCell)
						|| isCleanTop(lastCell, currentCell)
						|| isCleanBottom(lastCell, currentCell)
						|| isCleanRight(lastCell, currentCell) || isCleanLeft(
							lastCell, currentCell)))
		{
			return true;
		}

		return false;
	}

	private boolean isCleanBottom(Cell lastCell, Cell currentCell)
	{
		// Utils.debug("check isCleanBottom");
		if (lastCell.getRow() == TOTAL_CELL_ROWS - 1)
		{
			return isCleanBottom(currentCell);
		}

		if (currentCell.getRow() == TOTAL_CELL_ROWS - 1)
		{
			return isCleanBottom(lastCell);
		}

		Cell cellFrom;
		Cell cellTo;
		if (lastCell.getRow() < currentCell.getRow())
		{
			cellFrom = lastCell;
			cellTo = currentCell;
		}
		else
		{
			cellFrom = currentCell;
			cellTo = lastCell;
		}

		if (!isCleanBetweenRow(cellFrom,
				new Cell(cellTo.getRow() + 1, cellFrom.getCol())))
		{
			return false;
		}

		if (isCleanBetweenCol(new Cell(cellTo.getRow(), cellFrom.getCol()),
				cellTo))
		{
			// Utils.debug("isCleanBottom isCleanBetween *");
			return true;
		}

		for (int indexRow = cellTo.getRow() + 1; indexRow < TOTAL_CELL_ROWS; indexRow++)
		{
			if (!isCellEmpty(indexRow, cellFrom.getCol())
					|| !isCellEmpty(indexRow, cellTo.getCol()))
			{
				return false;
			}

			if (indexRow == TOTAL_CELL_ROWS - 1
					|| isCleanBetweenCol(new Cell(indexRow, cellFrom.getCol()),
							new Cell(indexRow, cellTo.getCol())))
			{
				// Utils.debug("isCleanBottom isCleanBetween");
				return true;
			}
		}

		return false;
	}

	private boolean isCleanTop(Cell lastCell, Cell currentCell)
	{
		// Utils.debug("check isCleanTop");
		if (lastCell.getRow() == 0)
		{
			return isCleanTop(currentCell);
		}

		if (currentCell.getRow() == 0)
		{
			return isCleanTop(lastCell);
		}

		Cell cellFrom;
		Cell cellTo;
		if (lastCell.getRow() > currentCell.getRow())
		{
			cellFrom = lastCell;
			cellTo = currentCell;
		}
		else
		{
			cellFrom = currentCell;
			cellTo = lastCell;
		}

		if (!isCleanBetweenRow(cellFrom,
				new Cell(cellTo.getRow() - 1, cellFrom.getCol())))
		{
			return false;
		}

		if (isCleanBetweenCol(new Cell(cellTo.getRow(), cellFrom.getCol()),
				cellTo))
		{
			// Utils.debug("isCleanTop isCleanBetween");
			return true;
		}

		for (int indexRow = cellTo.getRow() - 1; indexRow >= 0; indexRow--)
		{
			if (!isCellEmpty(indexRow, cellFrom.getCol())
					|| !isCellEmpty(indexRow, cellTo.getCol()))
			{
				return false;
			}

			if (indexRow == 0
					|| isCleanBetweenCol(new Cell(indexRow, cellFrom.getCol()),
							new Cell(indexRow, cellTo.getCol())))
			{
				// Utils.debug("isCleanTop isCleanBetween *");
				return true;
			}
		}

		return false;
	}

	private boolean isCleanRight(Cell lastCell, Cell currentCell)
	{
		// Utils.debug("check isCleanRight");
		if (lastCell.getCol() == TOTAL_CELL_COLUMNS - 1)
		{
			return isCleanRight(currentCell);
		}

		if (currentCell.getCol() == TOTAL_CELL_COLUMNS - 1)
		{
			return isCleanRight(lastCell);
		}

		Cell cellFrom;
		Cell cellTo;
		if (lastCell.getCol() < currentCell.getCol())
		{
			cellFrom = lastCell;
			cellTo = currentCell;
		}
		else
		{
			cellFrom = currentCell;
			cellTo = lastCell;
		}

		if (!isCleanBetweenCol(cellFrom,
				new Cell(cellFrom.getRow(), cellTo.getCol() + 1)))
		{
			return false;
		}

		if (isCleanBetweenRow(new Cell(cellFrom.getRow(), cellTo.getCol()),
				cellTo))
		{
			// Utils.debug("isCleanRight isCleanBetween");
			return true;
		}

		for (int indexCol = cellTo.getCol() + 1; indexCol < TOTAL_CELL_COLUMNS; indexCol++)
		{
			if (!isCellEmpty(cellFrom.getRow(), indexCol)
					|| !isCellEmpty(cellTo.getRow(), indexCol))
			{
				return false;
			}

			if (indexCol == TOTAL_CELL_COLUMNS - 1
					|| isCleanBetweenRow(new Cell(cellFrom.getRow(), indexCol),
							new Cell(cellTo.getRow(), indexCol)))
			{

				// Utils.debug("isCleanRight isCleanBetween *");
				return true;
			}
		}

		return false;
	}

	private boolean isCleanLeft(Cell lastCell, Cell currentCell)
	{
		// Utils.debug("check isCleanLeft");
		if (lastCell.getCol() == 0)
		{
			return isCleanLeft(currentCell);
		}

		if (currentCell.getCol() == 0)
		{
			return isCleanLeft(lastCell);
		}

		Cell cellFrom;
		Cell cellTo;
		if (lastCell.getCol() > currentCell.getCol())
		{
			cellFrom = lastCell;
			cellTo = currentCell;
		}
		else
		{
			cellFrom = currentCell;
			cellTo = lastCell;
		}

		if (!isCleanBetweenCol(cellFrom,
				new Cell(cellFrom.getRow(), cellTo.getCol() - 1)))
		{
			return false;
		}

		if (isCleanBetweenRow(new Cell(cellFrom.getRow(), cellTo.getCol()),
				cellTo))
		{
			// Utils.debug("isCleanLeft isCleanBetween *");
			return true;
		}

		for (int indexCol = cellTo.getCol() - 1; indexCol >= 0; indexCol--)
		{
			if (!isCellEmpty(cellFrom.getRow(), indexCol)
					|| !isCellEmpty(cellTo.getRow(), indexCol))
			{
				return false;
			}

			if (indexCol == 0
					|| isCleanBetweenRow(new Cell(cellFrom.getRow(), indexCol),
							new Cell(cellTo.getRow(), indexCol)))
			{
				// Utils.debug("isCleanLeft isCleanBetween");
				return true;
			}
		}

		return false;
	}

	private boolean isCleanBottom(Cell cell)
	{
		if (cell.getRow() != TOTAL_CELL_ROWS - 1)
		{
			for (int index = cell.getRow() + 1; index < TOTAL_CELL_ROWS; index++)
			{
				if (!isCellEmpty(index, cell.getCol()))
				{
					// Utils.debug("isCleanBottom wall");
					return false;
				}
			}
		}
		return true;
	}

	private boolean isCleanTop(Cell cell)
	{
		for (int index = 0; index < cell.getRow(); index++)
		{
			if (!isCellEmpty(index, cell.getCol()))
			{
				// Utils.debug("isCleanTop wall");
				return false;
			}
		}
		return true;
	}

	private boolean isCleanLeft(Cell cell)
	{
		for (int index = 0; index < cell.getCol(); index++)
		{
			if (!isCellEmpty(cell.getRow(), index))
			{
				// Utils.debug("isCleanLeft wall");
				return false;
			}
		}
		return true;
	}

	private boolean isCleanRight(Cell cell)
	{
		if (cell.getCol() != TOTAL_CELL_COLUMNS - 1)
		{
			for (int index = cell.getCol() + 1; index < TOTAL_CELL_COLUMNS; index++)
			{
				if (!isCellEmpty(cell.getRow(), index))
				{
					// Utils.debug("isCleanRight wall");
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Check the between space of 2 cells 00 and 11
	 * 
	 * A---A---A---A---A---A ********************************************
	 * A---00--A---A---A---A ********************************************
	 * A---A---A---A---A---A ********************************************
	 * A---X---A---X---A---A ********************************************
	 * A---A---A---A---A---A ********************************************
	 * A---A---A---A---11--A ********************************************
	 * 
	 * 00 (FROM_ROW, TO_ROW)
	 * 
	 * 11 (FROM_COLUMN, TO_COLUMN)
	 * */
	private boolean isCleanBetween(Cell lastCell, Cell currentCell)
	{
		/* Same row */
		if (lastCell.getRow() == currentCell.getRow())
		{
			return isCleanBetweenCol(lastCell, currentCell);
		}

		/* Same column */
		if (lastCell.getCol() == currentCell.getCol())
		{
			return isCleanBetweenRow(lastCell, currentCell);
		}

		final int FROM_ROW;
		final int TO_ROW;

		if (lastCell.getRow() < currentCell.getRow())
		{
			FROM_ROW = lastCell.getRow();
			TO_ROW = currentCell.getRow();
		}
		else
		{
			TO_ROW = lastCell.getRow();
			FROM_ROW = currentCell.getRow();
		}

		final int FROM_COLUMN;
		final int TO_COLUMN;

		if (lastCell.getCol() < currentCell.getCol())
		{
			FROM_COLUMN = lastCell.getCol();
			TO_COLUMN = currentCell.getCol();
		}
		else
		{
			TO_COLUMN = lastCell.getCol();
			FROM_COLUMN = currentCell.getCol();
		}

		boolean isRightDirection = lastCell.getRow() < currentCell.getRow()
				&& lastCell.getCol() < currentCell.getCol()
				|| currentCell.getRow() < lastCell.getRow()
				&& currentCell.getCol() < lastCell.getCol();

		if (isRightDirection)
		{
			if (!isCellEmpty(FROM_ROW, FROM_COLUMN + 1)
					&& !isCellEmpty(FROM_ROW + 1, FROM_COLUMN)
					|| !isCellEmpty(TO_ROW, TO_COLUMN - 1)
					&& !isCellEmpty(TO_ROW - 1, TO_COLUMN))
			{
				return false;
			}

			for (int indexRow = FROM_ROW; indexRow <= TO_ROW; indexRow++)
			{
				if (indexRow != FROM_ROW)
				{
					if (!isCellEmpty(indexRow, FROM_COLUMN))
					{
						return false;
					}
				}

				boolean isValidBefore = true;
				for (int indexCol = FROM_COLUMN + 1; indexCol < TO_COLUMN; indexCol++)
				{
					if (!isCellEmpty(indexRow, indexCol))
					{
						if (indexRow == TO_ROW)
						{
							return false;
						}
						else
						{
							isValidBefore = false;
							break;
						}
					}

					if (indexRow == FROM_ROW)
					{
						boolean isValidBeforeSub = true;
						for (int indexSubRow = FROM_ROW + 1; indexSubRow < TO_ROW; indexSubRow++)
						{
							if (!isCellEmpty(indexSubRow, indexCol))
							{
								isValidBeforeSub = false;
								break;
							}
						}

						if (isValidBeforeSub)
						{
							for (int indexSubCol = indexCol; indexSubCol < TO_COLUMN; indexSubCol++)
							{
								if (!isCellEmpty(TO_ROW, indexSubCol))
								{
									break;
								}

								if (indexSubCol == TO_COLUMN - 1)
								{
									// Utils.debug("isCleanBetween redirect cross");
									return true;
								}
							}
						}
					}
				}

				if (isValidBefore)
				{
					for (int indexSubRow = indexRow; indexSubRow < TO_ROW; indexSubRow++)
					{
						if (!isCellEmpty(indexSubRow, TO_COLUMN))
						{
							break;
						}

						if (indexSubRow == TO_ROW - 1)
						{
							// Utils.debug("isCleanBetween redirect cross---");
							return true;
						}
					}
				}
			}
		}
		else
		{
			if (!isCellEmpty(TO_ROW - 1, FROM_COLUMN)
					&& !isCellEmpty(TO_ROW, FROM_COLUMN + 1)
					|| !isCellEmpty(FROM_ROW, TO_COLUMN - 1)
					&& !isCellEmpty(FROM_ROW + 1, TO_COLUMN))
			{
				return false;
			}

			for (int indexRow = TO_ROW; indexRow >= FROM_ROW; indexRow--)
			{
				if (indexRow != TO_ROW)
				{
					if (!isCellEmpty(indexRow, FROM_COLUMN))
					{
						return false;
					}
				}

				boolean isValidBefore = true;
				for (int indexCol = FROM_COLUMN + 1; indexCol < TO_COLUMN; indexCol++)
				{
					if (!isCellEmpty(indexRow, indexCol))
					{
						if (indexRow == FROM_ROW)
						{
							return false;
						}
						else
						{
							isValidBefore = false;
							break;
						}
					}

					if (indexRow == TO_ROW)
					{
						boolean isValidBeforeSub = true;
						for (int indexSubRow = TO_ROW - 1; indexSubRow > FROM_ROW; indexSubRow--)
						{
							if (!isCellEmpty(indexSubRow, indexCol))
							{
								isValidBeforeSub = false;
								break;
							}
						}

						if (isValidBeforeSub)
						{
							for (int indexSubCol = indexCol; indexSubCol < TO_COLUMN; indexSubCol++)
							{
								if (!isCellEmpty(FROM_ROW, indexSubCol))
								{
									break;
								}

								if (indexSubCol == TO_COLUMN - 1)
								{
									// Utils.debug("isCleanBetween unredirect cross");
									return true;
								}
							}
						}
					}
				}

				if (isValidBefore)
				{
					for (int indexSubRow = indexRow; indexSubRow > FROM_ROW; indexSubRow--)
					{
						if (!isCellEmpty(indexSubRow, TO_COLUMN))
						{
							break;
						}

						if (indexSubRow == FROM_ROW + 1)
						{
							// Utils.debug("isCleanBetween unredirect cross--");
							return true;
						}
					}
				}
			}
		}

		// Utils.debug("isCleanBetween cross");
		return true;

	}

	private boolean isCleanBetweenRow(Cell lastCell, Cell currentCell)
	{
		int from;
		int to;

		if (lastCell.getRow() < currentCell.getRow())
		{
			from = lastCell.getRow();
			to = currentCell.getRow();
		}
		else
		{
			to = lastCell.getRow();
			from = currentCell.getRow();
		}

		for (int index = from + 1; index < to; index++)
		{
			if (!isCellEmpty(index, lastCell.getCol()))
			{
				return false;
			}
		}
		// Utils.debug("isCleanBetweenRow");
		return true;
	}

	private boolean isCleanBetweenCol(Cell lastCell, Cell currentCell)
	{
		int from;
		int to;

		if (lastCell.getCol() < currentCell.getCol())
		{
			from = lastCell.getCol();
			to = currentCell.getCol();
		}
		else
		{
			to = lastCell.getCol();
			from = currentCell.getCol();
		}

		for (int index = from + 1; index < to; index++)
		{
			if (!isCellEmpty(lastCell.getRow(), index))
			{
				return false;
			}
		}
		// Utils.debug("isCleanBetweenCol");
		return true;
	}

	public boolean isCellEmpty(Cell cell)
	{
		return mCellIdsArray[cell.getRow()][cell.getCol()].isEmpty();
	}

	public boolean isCellEmpty(int row, int col)
	{
		return mCellIdsArray[row][col].isEmpty();
	}

	public int getTotalTime()
	{
		return mTotalTime;
	}

	public void setTotalTime(int mTotalTime)
	{
		this.mTotalTime = mTotalTime;
	}

	public int getMiniLevel()
	{
		return mMiniLevel;
	}

	public void setMiniLevel(int mMiniLevel)
	{
		this.mMiniLevel = mMiniLevel;
	}

	public List<Cell> getListPointsOfValue(int value)
	{
		List<Cell> listPoints = new ArrayList<Cell>();

		for (int row = 0; row < TOTAL_CELL_ROWS; row++)
		{
			for (int col = 0; col < TOTAL_CELL_COLUMNS; col++)
			{
				if (mCellIdsArray[row][col].getValue() == value)
				{
					listPoints.add(new Cell(row, col));
				}
			}
		}
		return listPoints;
	}

	public List<Cell> getListPointsOfValueNext(Cell cell, int index)
	{
		List<Cell> listPoints = new ArrayList<Cell>();
		Value value = mCellIdsArray[cell.getRow()][cell.getCol()];

		for (int indexRemainCell = index + 1; indexRemainCell < mRemainCell.size(); indexRemainCell++)
		{
			Cell cellRemain = mRemainCell.get(indexRemainCell);
			if (mCellIdsArray[cellRemain.getRow()][cellRemain.getCol()].getValue() == value.getValue())
			{
				listPoints.add(cellRemain);
			}
		}
		
		return listPoints;
	}

	private boolean isRunnableToOther(Cell cellTarget, int index)
	{
		List<Cell> listCells = getListPointsOfValueNext(cellTarget, index);

		for (Cell cell : listCells)
		{
			if (isRightMove(cellTarget, cell))
			{
				mCellIdsArray[cell.getRow()][cell.getCol()].setRunnable(true);
				mCellIdsArray[cellTarget.getRow()][cellTarget.getCol()].setRunnable(true);

				mRunnableCells = new Cell[] { cell, cellTarget };

				Utils.debug("mRunnableCells = (" + cell.getRow() + ","
						+ cell.getCol() + ") and (" + cellTarget.getRow() + ","
						+ cellTarget.getCol() + ")");

				return true;
			}
		}

		return false;
	}

	public boolean isRunnable()
	{
		return mRunnableCells != null;
	}

	public void makeRunnable()
	{
		Utils.debug("makeRunnable");
		List<Value> idsList = new ArrayList<Value>();
		List<Cell> cellsList = new ArrayList<Cell>();

		for (int indexRow = 0; indexRow < TOTAL_CELL_ROWS; indexRow++)
		{
			for (int indexCol = 0; indexCol < TOTAL_CELL_COLUMNS; indexCol++)
			{
				if (!mCellIdsArray[indexRow][indexCol].isEmpty())
				{
					idsList.add(mCellIdsArray[indexRow][indexCol]);
					cellsList.add(new Cell(indexRow, indexCol));
				}
			}
		}

		while (!idsList.isEmpty())
		{
			int position = com.tsm.picachu.common.Utils.getNumberRandom(idsList.size());
			Cell cell = cellsList.get(0);
			mCellIdsArray[cell.getRow()][cell.getCol()].setValue(idsList.get(
					position).getValue());
			idsList.remove(position);
			cellsList.remove(0);
		}
	}

	public void findRunnableCells()
	{
		Utils.debug("findRunnableCells");
		for (int indexRemainCell = 0; indexRemainCell < mRemainCell.size(); indexRemainCell++)
		{
			Cell cell = mRemainCell.get(indexRemainCell);
			Value value = mCellIdsArray[cell.getRow()][cell.getCol()];
			value.setRunnable(false);

			if (value.isEmpty())
			{
				continue;
			}

			if (isRunnableToOther(cell, indexRemainCell))
			{
				return;
			}

		}
	}

	public boolean isSameRunnable(Cell cell1, Cell cell2)
	{
		return mRunnableCells == null || mRunnableCells[0].isSame(cell1)
				|| mRunnableCells[0].isSame(cell2)
				|| mRunnableCells[1].isSame(cell1)
				|| mRunnableCells[1].isSame(cell2);
	}

	public void clearRunnable()
	{
		mRunnableCells = null;
	}
}
